/**
 * Copyright (c) 2020 Gitpod GmbH. All rights reserved.
 * Licensed under the GNU Affero General Public License (AGPL).
 * See License-AGPL.txt in the project root for license information.
 */

import { WorkspaceContext, User } from "@gitpod/gitpod-protocol";
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
import { inject, injectable } from "inversify";
import { AuthProviderParams } from "../auth/auth-provider";
import { URLSearchParams, URL } from "url";

export interface IContextParser {
    normalize?(contextURL: string): string | undefined
    canHandle(user: User, context: string): boolean
    handle(ctx: TraceContext, user: User, context: string): Promise<WorkspaceContext>
}
export const IContextParser = Symbol("IContextParser")

@injectable()
export abstract class AbstractContextParser implements IContextParser {

    @inject(AuthProviderParams) protected config: AuthProviderParams;

    public normalize(contextUrl: string): string | undefined {
        let url = contextUrl.trim();
        if (url.startsWith(`${this.config.host}/`)) {
            url = `https://${url}`;
        }
        if (url.startsWith(`https://${this.config.host}/`)) {
            return url;
        }
        return undefined;
    }

    public canHandle(user: User, contextUrl: string): boolean {
        return this.normalize(contextUrl) !== undefined;
    }

    public async parseURL(user: User, contextUrl: string): Promise<URLParts> {
        const url = new URL(contextUrl);
        const pathname = url.pathname.replace(/^\//, "").replace(/\/$/, ""); // pathname without leading and trailing slash
        const segments = pathname.split('/');
        var owner: string = segments[0];
        var repoName: string = segments[1];
        var moreSegmentsStart: number = 2;
        const endsWithRepoName = segments.length === moreSegmentsStart;
        const searchParams = url.searchParams;
        return {
            host: url.hostname,
            owner: owner,
            repoName: this.parseRepoName(repoName, endsWithRepoName),
            moreSegments: endsWithRepoName ? [] : segments.slice(moreSegmentsStart),
            searchParams
        }
    }

    protected parseRepoName(urlSegment: string, lastSegment: boolean): string {
        return lastSegment && urlSegment.endsWith('.git') ? urlSegment.substring(0, urlSegment.length - '.git'.length) : urlSegment;
    }

    public abstract handle(ctx: TraceContext, user: User, context: string): Promise<WorkspaceContext>;
}

export interface URLParts {
    host: string;
    owner: string;
    repoName: string;
    moreSegments: string[];
    searchParams: URLSearchParams;
}

/**
 * Prefix parser consume a segment of the context string, expect
 * the remainder to go through regular context parsing, and be returned
 * to them. For example, the prebuild context parser works that way: it
 * handles context strings like /prebuild/<othercontext>, consumes "prebuild"
 * and expects "othercontext" to be parsed and passed back.
 */
export interface IPrefixContextParser {
    normalize?(contextURL: string): string | undefined
    findPrefix(user: User, context: string): string | undefined
    handle(user: User, prefix: string, context: WorkspaceContext): Promise<WorkspaceContext>
}
export const IPrefixContextParser = Symbol("IPrefixContextParser")

export namespace IssueContexts {
    export function toBranchName(user: User, issueTitle: string, issueNr: number): string {
        const titleWords = issueTitle.toLowerCase().replace(/[^a-z]/g, '-').split('-').filter(w => w.length > 0)
        let localBranch = (user.name + '/').toLowerCase();
        for (const segment of titleWords) {
            if (localBranch.length > 30) {
                break;
            }
            localBranch += segment + '-';
        }
        localBranch += issueNr;
        return localBranch;
    }
}
